package com.example.redis.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 缓存键生成器
 * 根据业务code和请求参数生成缓存键
 * 支持Map类型参数的缓存键生成
 *
 * @author example
 */
@Component
public class CacheKeyGenerator implements KeyGenerator {

    private static final String KEY_SEPARATOR = ":";
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Object generate(Object target, Method method, Object... params) {
        // 获取业务code（可以从方法名、类名或参数中提取）
        String businessCode = extractBusinessCode(target, method, params);

        // 生成参数部分的键
        String paramKey = generateParamKey(params);

        // 组合成最终的缓存键
        return businessCode + KEY_SEPARATOR + paramKey;
    }

    /**
     * 提取业务code
     */
    private String extractBusinessCode(Object target, Method method, Object... params) {
        // 方法1：从方法名提取
        String methodName = method.getName();
        if (methodName.startsWith("get") || methodName.startsWith("find") || methodName.startsWith("query")) {
            return methodName;
        }

        // 方法2：从类名提取
        String className = target.getClass().getSimpleName();
        if (className.endsWith("Service") || className.endsWith("Controller")) {
            return className;
        }

        // 方法3：从参数中提取（如果第一个参数是业务code）
        if (params.length > 0 && params[0] instanceof String) {
            String firstParam = (String) params[0];
            if (StringUtils.isNotBlank(firstParam) && firstParam.length() <= 20) {
                return firstParam;
            }
        }

        // 默认返回方法名
        return methodName;
    }

    /**
     * 生成参数部分的键
     */
    private String generateParamKey(Object... params) {
        if (params == null || params.length == 0) {
            return "empty";
        }

        return Arrays.stream(params)
                .map(this::paramToString)
                .collect(Collectors.joining(KEY_SEPARATOR));
    }

    /**
     * 将参数转换为字符串
     * 增强了对Map类型的处理
     */
    private String paramToString(Object param) {
        if (param == null) {
            return "null";
        }

        if (param instanceof String) {
            return (String) param;
        }

        if (param instanceof Number) {
            return param.toString();
        }

        // 特殊处理Map类型
        if (param instanceof Map) {
            return mapToString((Map<?, ?>) param);
        }

        // 特殊处理List类型
        if (param instanceof List) {
            return listToString((List<?>) param);
        }

        // 对于其他复杂对象，使用hashCode
        return String.valueOf(param.hashCode());
    }

    /**
     * 将Map转换为字符串，用于缓存键生成
     * 确保相同内容的Map生成相同的键
     */
    private String mapToString(Map<?, ?> map) {
        if (map == null || map.isEmpty()) {
            return "empty_map";
        }

        try {
            // 方法1：使用JSON序列化（推荐，保证一致性）
            return objectMapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            // 方法2：如果JSON序列化失败，使用排序后的键值对
            return map.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey((Comparator<Object>) (o1, o2) -> {
                        if (o1 == null && o2 == null) return 0;
                        if (o1 == null) return -1;
                        if (o2 == null) return 1;
                        return o1.toString().compareTo(o2.toString());
                    }))
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.joining(","));
        }
    }

    /**
     * 将List转换为字符串，用于缓存键生成
     */
    private String listToString(List<?> list) {
        if (list == null || list.isEmpty()) {
            return "empty_list";
        }

        try {
            // 使用JSON序列化保证一致性
            return objectMapper.writeValueAsString(list);
        } catch (JsonProcessingException e) {
            // 备用方案：直接转换为字符串
            return list.toString();
        }
    }

    /**
     * 手动生成缓存键的方法
     */
    public static String generateKey(String businessCode, Object... params) {
        CacheKeyGenerator generator = new CacheKeyGenerator();
        return (String) generator.generate(null, null, params);
    }

    /**
     * 专门为Map参数生成缓存键的方法
     */
    public static String generateMapKey(String businessCode, Map<String, Object> params) {
        CacheKeyGenerator generator = new CacheKeyGenerator();
        return businessCode + KEY_SEPARATOR + generator.mapToString(params);
    }

    /**
     * 生成带前缀的Map缓存键
     */
    public static String generateMapKeyWithPrefix(String prefix, String businessCode, Map<String, Object> params) {
        return prefix + ":" + generateMapKey(businessCode, params);
    }
}
